/*****************************************************************************
 * DotNetProfiler
 * 
 * Copyright (c) 2006 Scott Hackett
 * 
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the author be held liable for any damages arising from the
 * use of this software. Permission to use, copy, modify, distribute and sell
 * this software for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.
 * 
 * Scott Hackett (code@scotthackett.com)
 *****************************************************************************/

#pragma once
#include "resource.h"
#include "BoneheadRuntime.h"
#include "CorProfilerCallbackImpl.h"
#include "resource.h"
#include <set>

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

#define ASSERT_HR(x) _ASSERT(SUCCEEDED(x))
#define NAME_BUFFER_SIZE 1024

// CProfiler
class ATL_NO_VTABLE CProfiler :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CProfiler, &CLSID_BoneheadRuntime>,
	public CCorProfilerCallbackImpl
{
public:
	CProfiler();

	DECLARE_REGISTRY_RESOURCEID(IDR_PROFILER)
	BEGIN_COM_MAP(CProfiler)
		COM_INTERFACE_ENTRY(ICorProfilerCallback)
		COM_INTERFACE_ENTRY(ICorProfilerCallback2)
	END_COM_MAP()
	DECLARE_PROTECT_FINAL_CONSTRUCT()

	// overridden implementations of FinalConstruct and FinalRelease
	HRESULT FinalConstruct();
	void FinalRelease();

    // STARTUP/SHUTDOWN EVENTS
    STDMETHOD(Initialize)(IUnknown *pICorProfilerInfoUnk);
    STDMETHOD(Shutdown)();

	// callback functions
	void Enter(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo);
	void Leave(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo, COR_PRF_FUNCTION_ARGUMENT_RANGE *argumentRange);
	void Tailcall(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo);
	
	// exception handling.
    STDMETHOD(ExceptionThrown)(ObjectID thrownObjectID);
    STDMETHOD(ExceptionUnwindFunctionEnter)(FunctionID functionID);
    STDMETHOD(ExceptionUnwindFunctionLeave)();

	STDMETHOD(UnmanagedToManagedTransition)(FunctionID functionID, COR_PRF_TRANSITION_REASON reason);
	STDMETHOD(ManagedToUnmanagedTransition)(FunctionID functionID, COR_PRF_TRANSITION_REASON reason);

	// mapping functions
	static UINT_PTR _stdcall FunctionMapper(FunctionID functionId, BOOL *pbHookFunction);
	void MapFunction(FunctionID);

private:
	enum MessageID
	{
		MSG_ENTER						 = 0,
		MSG_LEAVE						 = 1,
		MSG_TAIL					     = 2,
		MSG_ManagedToUnmanagedTransition = 3,
		MSG_UnmanagedToManagedTransition = 4,
		MSG_ExceptionUnwindFunctionEnter = 5,
		MSG_ExceptionUnwindFunctionLeave = 6,
        MSG_ExceptionThrown              = 7,
	};

#pragma pack(push, 1)
	struct Sample
	{
		__int64 mThread;
		__int64 mFunction;
		__int64 mCycle;
		__int32 mMessage;
	};
	struct Method
	{
		__int64 mFunction;
        char    mText[512];
	};
#pragma pack(pop)

    enum { NUM_SAMPLES = 10000 };
    enum { NUM_METHODS = 10000 };

	CComQIPtr<ICorProfilerInfo>		mICorProfilerInfo;
	CComQIPtr<ICorProfilerInfo2>	mICorProfilerInfo2;
	std::set<FunctionID>			mFunctionMap;
	FILE*							mSampleFile;
    FILE*							mMethodFile;
    CRITICAL_SECTION                mLock;
    Sample                          mSamples[NUM_SAMPLES];
    Method                          mMethods[NUM_METHODS];
    int                             mNumSamples;
    int                             mNumMethods;
    __int64                         mTimeOffset;

    void    LogSample(MessageID aMessage, FunctionID aFunctionID);
	void    LogMethod(FunctionID aFunctionID, const char* aString);
	HRESULT GetFullMethodName(FunctionID functionId, LPWSTR wszMethod, int cMethod );
	HRESULT SetEventMask();
};

OBJECT_ENTRY_AUTO(__uuidof(BoneheadRuntime), CProfiler)
